/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.contacts.common.list;

import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.widget.ListView;

/**
 * A ListView that can be asked to scroll (smoothly or otherwise) to a specific position. This class
 * takes advantage of similar functionality that exists in {@link ListView} and enhances it.
 */
public class AutoScrollListView extends ListView {

  /** Position the element at about 1/3 of the list height */
  private static final float PREFERRED_SELECTION_OFFSET_FROM_TOP = 0.33f;

  private int mRequestedScrollPosition = -1;
  private boolean mSmoothScrollRequested;

  public AutoScrollListView(Context context) {
    super(context);
  }

  public AutoScrollListView(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public AutoScrollListView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  }

  /**
   * Brings the specified position to view by optionally performing a jump-scroll maneuver: first it
   * jumps to some position near the one requested and then does a smooth scroll to the requested
   * position. This creates an impression of full smooth scrolling without actually traversing the
   * entire list. If smooth scrolling is not requested, instantly positions the requested item at a
   * preferred offset.
   */
  public void requestPositionToScreen(int position, boolean smoothScroll) {
    mRequestedScrollPosition = position;
    mSmoothScrollRequested = smoothScroll;
    requestLayout();
  }

  @Override
  protected void layoutChildren() {
    super.layoutChildren();
    if (mRequestedScrollPosition == -1) {
      return;
    }

    final int position = mRequestedScrollPosition;
    mRequestedScrollPosition = -1;

    int firstPosition = getFirstVisiblePosition() + 1;
    int lastPosition = getLastVisiblePosition();
    if (position >= firstPosition && position <= lastPosition) {
      return; // Already on screen
    }

    final int offset = (int) (getHeight() * PREFERRED_SELECTION_OFFSET_FROM_TOP);
    if (!mSmoothScrollRequested) {
      setSelectionFromTop(position, offset);

      // Since we have changed the scrolling position, we need to redo child layout
      // Calling "requestLayout" in the middle of a layout pass has no effect,
      // so we call layoutChildren explicitly
      super.layoutChildren();

    } else {
      // We will first position the list a couple of screens before or after
      // the new selection and then scroll smoothly to it.
      int twoScreens = (lastPosition - firstPosition) * 2;
      int preliminaryPosition;
      if (position < firstPosition) {
        preliminaryPosition = position + twoScreens;
        if (preliminaryPosition >= getCount()) {
          preliminaryPosition = getCount() - 1;
        }
        if (preliminaryPosition < firstPosition) {
          setSelection(preliminaryPosition);
          super.layoutChildren();
        }
      } else {
        preliminaryPosition = position - twoScreens;
        if (preliminaryPosition < 0) {
          preliminaryPosition = 0;
        }
        if (preliminaryPosition > lastPosition) {
          setSelection(preliminaryPosition);
          super.layoutChildren();
        }
      }

      smoothScrollToPositionFromTop(position, offset);
    }
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);

    // Workaround for b/31160338 and b/32778636.
    if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N
        || android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) {
      layoutChildren();
    }
  }
}
